package com.zzf.sensitive.aop;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.zzf.sensitive.util.AseUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 敏感字段加密解密切面
 *
 * @author zhangzhifu
 * @date 2021/1/15
 */
@Order(Ordered.HIGHEST_PRECEDENCE)
@Aspect
@Component
@Slf4j
public class SensitiveFieldAop {
    @Value("${secretkey}")
    private String secretKey;

    @Pointcut("@annotation(com.zzf.sensitive.aop.SensitiveMethod)")
    public void annotationPointCut() {}

    @Around("annotationPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        Object responseObj = null;
        try {
            if (!(joinPoint.getSignature() instanceof MethodSignature)){
                return null;
            }
            MethodSignature signature = (MethodSignature)joinPoint.getSignature();
            SensitiveMethod methodAnnotation = signature.getMethod().getAnnotation(SensitiveMethod.class);

            // 入参解密
            if (methodAnnotation.inDecode()) {
                Object[] requestPrams = joinPoint.getArgs();
                this.decryptRequestPrams(requestPrams);
            }
            responseObj = joinPoint.proceed();
            // 出参加密
            if (methodAnnotation.outEncode()) {
                this.encryptResponsePram(responseObj);
            }

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            log.error("SecureFieldAop处理出现异常{}", e);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            log.error("SecureFieldAop处理出现异常{}", throwable);
        }
        return responseObj;
    }

    /**
     * 入参解密
     *
     * @param requestPrams
     */
    private void decryptRequestPrams(Object[] requestPrams) throws IllegalAccessException {
        if (requestPrams != null || requestPrams.length > 0) {
            for (Object requestObj : requestPrams) {
                if (requestObj instanceof Collection) {
                    Collection collection = (Collection)requestObj;
                    collection.forEach(var -> {
                        try {
                            handleDecrypt(var, true);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    });
                } else {
                    this.handleDecrypt(requestObj, true);
                }
            }
        }
    }

    /**
     * 出参加密 or 打码
     *
     * @param responseObj
     */
    private void encryptResponsePram(Object responseObj) throws IllegalAccessException {
        if (responseObj == null) {
            return;
        }
        Map<String, Object> map = this.beanToMap(responseObj);
        Object responseData = map.get("data");
        if (responseData == null) {
            return;
        }
        if (responseData instanceof Collection) {
            Collection collection = (Collection)responseData;
            collection.forEach(var -> {
                try {
                    handleDecrypt(var, false);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            });
        } else {
            handleDecrypt(responseData, false);
        }
    }

    /**
     * 处理加解密
     *
     * @param obj
     * @param inFlag true：解密，false：加密
     * @return {@link Object}
     * @author zhangzhifu
     * @date 2021/1/15 16:36
     */
    private Object handleDecrypt(Object obj, boolean inFlag) throws IllegalAccessException {
        if (Objects.isNull(obj)) {
            return null;
        }
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            if (!(field.get(obj) instanceof String)){
                return null;
            }
            String encryptValue = (String)field.get(obj);
            boolean hasSecureField = field.isAnnotationPresent(SensitiveField.class);
            boolean hasMarkField = field.isAnnotationPresent(MarkField.class);
            //处理加/解密字段
            if (hasSecureField) {
                SensitiveField sensitiveFieldAnnotation = field.getAnnotation(SensitiveField.class);
                encryptValue=handSecureField(sensitiveFieldAnnotation, encryptValue,  inFlag);
            }
            //处理打码字段
            if (hasMarkField) {
                MarkField markFieldAnnotation = field.getAnnotation(MarkField.class);
                encryptValue=handMarkField(markFieldAnnotation, encryptValue, inFlag);
            }
            field.set(obj, encryptValue);

        }
        return obj;
    }

    private String handMarkField(MarkField markFieldAnnotation, String encryptValue,
        boolean inFlag) throws IllegalAccessException {
        try {
            switch (markFieldAnnotation.type()) {
                case NAME: {
                    System.out.println("NAME");
//                    plaintextValue = "MarkName";
                    // plaintextValue = AseUtil.encrypt(encryptValue, secretKey);
                    return "MarkName";
                }
                case PHONE: {
                    return AseUtil.encrypt(encryptValue, secretKey);
                }
                case ADDRESS: {
                    return AseUtil.encrypt(encryptValue, secretKey);
                }
                default:
            }
        } catch (Exception e) {
            log.error("敏感数据处理异常", e);
        }
        return encryptValue;
    }

    private String handSecureField(SensitiveField sensitiveFieldAnnotation, String encryptValue,
        boolean inFlag) throws IllegalAccessException {
        try {
            if (inFlag) {
                // 处理入参，解密
                switch (sensitiveFieldAnnotation.decryptType()) {
                    case AES: {
                        return AseUtil.decrypt(encryptValue, secretKey);
                    }
                    case RSA: {
                        return AseUtil.decrypt(encryptValue, secretKey);
                    }
                    default:
                }
            } else {// 处理出参，加密
                switch (sensitiveFieldAnnotation.encryptType()) {
                    case AES: {
                        return AseUtil.encrypt(encryptValue, secretKey);
                    }
                    case RSA: {
                        return AseUtil.encrypt(encryptValue, secretKey);
                    }
                    case PHONE_ENCRYPT: {
                        return AseUtil.encrypt(encryptValue, secretKey);
                    }
                    default:
                }
            }
        } catch (Exception e) {
            log.error("敏感数据处理异常", e);
        }
        return encryptValue;
    }

    /**
     * 将对象属性转化为map结合
     */
    private <T> Map<String, Object> beanToMap(T bean) {
        Map<String, Object> map = new HashMap<>();
        if (bean != null) {
            BeanMap beanMap = BeanMap.create(bean);
            for (Object key : beanMap.keySet()) {
                map.put(key + "", beanMap.get(key));
            }
        }
        return map;
    }
}
